using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;


namespace ChipSharp
{
    class World
    {

    /* .. public section */

        public enum Direction : int
        {
            kNone,
            kLeft = 1,
            kUp = 2,
            kRight = 4,
            kDown = 8,
        }

        public World(Game game, Renderer renderer)
        {
            myGame = game;
            myRenderer = renderer;

            renderer.World = this;
            CreateDefaultWorld();

            myCharps.Add(myMan);
            myMovables.Add(myMan);
        }

        ~World()
        {
        }

        public Charp[,] Map
        {
            get
            {
                return myMap;
            }
        }

        public CharpMan Man
        {
            get
            {
                return myMan;
            }
        }

        public Game Game
        {
            get
            {
                return myGame;
            }
        }

        public void UpdateView()
        {
            if (myMan != null)
            {
                myDrawLeft = Func<int>.Max(0, myMan.X - myDrawWidth / 2);
                myDrawTop = Func<int>.Max(0, myMan.Y - myDrawHeight / 2);
                if (myDrawLeft + myDrawWidth > myMap.GetUpperBound(1) + 1)
                {
                    myDrawLeft = myMap.GetUpperBound(1) - myDrawWidth + 1;
                }
                if (myDrawTop + myDrawHeight > myMap.GetUpperBound(0) + 1)
                {
                    myDrawTop = myMap.GetUpperBound(0) - myDrawHeight + 1;
                }
            }
        }

        class E : Exception
        {
        }

        public void Redraw()
        {
            Bitmap area = myRenderer.WorldArea;
            Mutex areaMutex = myRenderer.WorldAreaMutex;
            try
            {
                areaMutex.WaitOne();
                Graphics graph = Graphics.FromImage(area);
                if (graph != null)
                {
                    for (int i = 0; i < myDrawHeight; i++)
                    {
                        for (int j = 0; j < myDrawWidth; j++)
                        {
                            int nTileTop = i * area.Height / myDrawWidth;
                            int nTileLeft = j * area.Width / myDrawWidth;
                            int nTileBottom = (i + 1) * area.Height / myDrawWidth;
                            int nTileRight = (j + 1) * area.Height / myDrawWidth;
                            Rectangle tile = new Rectangle(nTileLeft, nTileTop,
                                nTileRight - nTileLeft, nTileBottom - nTileTop);
                            myMap[myDrawTop + i, myDrawLeft + j].Draw(graph, tile);
                        }
                    }
                }
            }
            finally
            {
                areaMutex.ReleaseMutex();
            }
        }

        public void UpdateObjects()
        {
            // update
            foreach (Charp charp in myCharps)
            {
                charp.Step(this);
            }

            // collision detection
            foreach (Movable m in myMovables)
            {
                if (!m.Processed)
                {
                    int x = m.X;
                    int y = m.Y;

                    myMap[y, x].SortPile(this);

                    Charp charp1 = myMap[y, x];

                    /**
                     * Motive physics has a very nice property which 
                     * indicates that the effect of a group of objects 
                     * colliding together can be decomposed into 
                     * collisions of the pairs
                     */
                    for (; charp1 != null; charp1 = charp1.Upper)
                    {
                        Movable m1 = charp1 as Movable;
                        if (m1 == null)
                        {
                            continue;
                        }

                        Charp charp2 = null;

                        switch (m1.VDir)
                        {
                            case Direction.kLeft:
                                if (x > 0)
                                    charp2 = myMap[y, x - 1];
                                break;
                            case Direction.kRight:
                                if (x + 1 < myMap.GetLength(1))
                                    charp2 = myMap[y, x + 1];
                                break;
                            case Direction.kUp:
                                if (y > 0)
                                    charp2 = myMap[y - 1, x];
                                break;
                            case Direction.kDown:
                                if (y + 1 < myMap.GetLength(0))
                                    charp2 = myMap[y, y + 1];
                                break;
                        }

                        for ( ; charp2 != null; charp2 = charp2.Upper)
                        {
                            Movable m2 = charp2 as Movable;
                            if (m2 == null)
                            {
                                continue;
                            }
                            m1.Collide(m2);
                        }

                        m1.Processed = true;
                    }
                }
            }
        }   /* public void UpdateObjects() */

    /* .. private section */

        private Charp[,] myMap = null;
        private CharpMan myMan = null;
        private List<Charp> myCharps = new List<Charp>();

        private List<Movable> myMovables = new List<Movable>();

        private Game myGame = null;
        private Renderer myRenderer = null;

        private int myDrawLeft = 0;
        private int myDrawTop = 0;
        private int myDrawWidth = 9;
        private int myDrawHeight = 9;

        /* .. Map construction and storing */
        private void CreateDefaultWorld()
        {
            int kDefHeight = CharpData.DefWorldPattern.Length;
            int kDefWidth = CharpData.DefWorldPattern[0].Length;
            myMap = new Charp[kDefHeight, kDefWidth];
            for (int i = 0; i < kDefHeight; i++)
            {
                for (int j = 0; j < kDefWidth; j++)
                {
                    char block = CharpData.DefWorldPattern[i][j];
                    if (block == '~')
                    {
                        myMap[i, j] = new Water(i, j);
                    }
                    else if (block == '*')
                    {
                        myMap[i, j] = new Ice(i, j);
                    }
                    else if (block == '`')
                    {
                        myMap[i, j] = new Rail(i, j, 0x4);
                    }
                    else if (block == '\'')
                    {
                        myMap[i, j] = new Rail(i, j, 0x8);
                    }
                    else if (block == '-')
                    {
                        myMap[i, j] = new Rail(i, j, 0x2);
                    }
                    else if (block == '_')
                    {
                        myMap[i, j] = new Rail(i, j, 0x1);
                    }
                    else
                    {
                        myMap[i, j] = new Tile(i, j);
                        if (block == 'M' && myMan == null)
                        {
                            myMap[i, j].Upper = myMan = new CharpMan(i, j);
                            UpdateView();
                        }
                        else if (block == 'o')
                        {
                            //myMap[i, j].Upper = new BouncingBall(i, j);
                        }
                    }
                }
            }
        }   /* private void CreateDefaultWorld() */

    }   /* public class World */
}
